package com.liuyjy.utils.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;

/**
 * Cookie工具类,封装Cookie常用操作
 *
 * @author Think
 */
@Slf4j
public class CookieUtil {

    /**
     * 设置cookie有效期，根据需要自定义[本系统设置为30天]
     */
    private final static int COOKIE_MAX_AGE = 1000 * 60 * 60 * 24 * 30;

    /**
     * 对字符串进行URL编码
     *
     * @param s 需要编码的字符串
     * @return String
     */
    public static String urlEncode(String s) {
        if (StringUtils.isNotBlank(s)) {
            try {
                return URLEncoder.encode(s, "UTF-8");
            } catch (UnsupportedEncodingException e) {
                log.error("处理失败", e);
            }
        }
        return s;
    }

    /**
     * 对URL进行URL解码
     *
     * @param s 需要解码的字符串
     * @return String
     */
    public static String urlDecode(String s) {
        if (StringUtils.isNotBlank(s)) {
            try {
                return URLDecoder.decode(s, "UTF-8");
            } catch (UnsupportedEncodingException e) {
                log.error("处理失败", e);
            }
        }
        return s;
    }

    /**
     * 根据cookie前缀获取cookies
     * 此方法用于从HTTP请求中筛选出以特定前缀开头的cookie，并返回一个包含这些cookie名称和值的映射
     * 为什么要筛选cookie？在某些情况下，应用程序可能只对具有特定前缀的cookie感兴趣，这些cookie可能用于特定的功能或配置
     * 通过筛选，可以减少数据量，提高处理效率，同时提高代码的可读性和可维护性
     *
     * @param request   HTTP请求对象，用于获取请求中的cookies
     * @param cookiePre cookie前缀，用于筛选哪些cookie应该被包含在返回的映射中
     * @return 返回一个映射，其中包含名称以给定前缀开头的cookie的名称和值
     */
    public static Map<String, String> getCookiesByPre(HttpServletRequest request, String cookiePre) {
        // 初始化一个空的映射，用于存储筛选后的cookie名称和值
        Map<String, String> sCookie = new HashMap<String, String>();

        // 获取请求中的所有cookies
        Cookie[] cookies = request.getCookies();

        // 检查cookies是否为空，如果为空，则直接返回空映射
        if (EmptyUtil.isEmpty(cookies)) {
            return sCookie;
        }

        // 获取cookie前缀的长度，用于后续从cookie名称中去除前缀
        int prelength = StringUtils.length(cookiePre);

        // 遍历所有cookies，寻找以指定前缀开头的cookie
        for (Cookie cookie : cookies) {
            String name = cookie.getName();

            // 如果当前cookie的名称不以指定前缀开头，则跳过此cookie，继续下一个循环
            if (!name.startsWith(cookiePre)) {
                continue;
            }

            // 从cookie名称中去除前缀，并将处理后的名称和cookie的值作为键值对存入映射中
            // 这里使用了urlDecode方法对cookie的值进行解码，以处理URL编码的问题
            sCookie.put(name.substring(prelength), urlDecode(cookie.getValue()));
        }

        // 返回包含筛选后cookie名称和值的映射
        return sCookie;
    }


    /**
     * 删除指定Cookie
     *
     * @param response HttpServletResponse对象，用于添加Cookie
     * @param cookie   要删除的Cookie对象
     *                 <p>
     *                 此方法通过设置Cookie的Max-Age为0来实现删除Cookie的操作，使得Cookie立即过期
     */
    public static void removeCookie(HttpServletResponse response, Cookie cookie) {
        removeCookie(response, cookie, null);
    }

    /**
     * 删除指定Cookie
     * 该方法通过设置Cookie的最大存活时间为0，来实现删除Cookie的目的
     *
     * @param response HTTP响应对象，用于向客户端发送Cookie
     * @param cookie   需要删除的Cookie对象
     * @param domain   Cookie的域，确保其只在指定的域名下有效
     */
    public static void removeCookie(HttpServletResponse response, Cookie cookie, String domain) {
        if (null == cookie) {
            return;
        }
        // 设置Cookie的路径为根路径，确保Cookie可以在整个域名下生效
        cookie.setPath("/");
        // 清空Cookie的值，使其不再携带任何信息
        cookie.setValue("");
        // 设置Cookie的最大存活时间为0秒，这将导致Cookie立即被删除
        cookie.setMaxAge(0);
        // 设置Cookie的域，确保其只在指定的域名下有效
        if (EmptyUtil.isNotEmpty(domain)) {
            cookie.setDomain(domain);
        }
        // 将修改后的Cookie添加到HTTP响应中，以实现删除操作
        response.addCookie(cookie);
    }

    /**
     * 根据Cookie名称得到Cookie的值，没有返回Null
     *
     * @param request HTTP请求对象，用于获取Cookie信息
     * @param name    Cookie的名称
     * @return 如果找到指定名称的Cookie，则返回其值；否则返回Null
     */
    public static String getCookieValue(HttpServletRequest request, String name) {
        if (EmptyUtil.isEmpty(name)) {
            return null;
        }
        Cookie cookie = getCookie(request, name);
        if (null == cookie) {
            return null;
        }
        return cookie.getValue();
    }

    /**
     * 根据Cookie名称得到Cookie对象，不存在该对象则返回Null
     * 此方法用于从HTTP请求中获取特定名称的Cookie对象
     * 它首先检查请求中的所有Cookie，然后通过名称和域来匹配所需的Cookie
     * 如果找到匹配的Cookie，则返回该Cookie对象；否则，返回Null
     *
     * @param request HTTP请求对象，用于获取请求中的Cookie数组
     * @param name    需要获取的Cookie的名称
     * @return 匹配的Cookie对象，如果未找到则返回Null
     */
    public static Cookie getCookie(HttpServletRequest request, String name) {
        // 获取请求中的所有Cookie
        Cookie[] cookies = request.getCookies();
        // 检查Cookie数组是否为空或名称是否为空
        if (EmptyUtil.isEmpty(cookies) || EmptyUtil.isEmpty(name)) {
            return null;
        }
        // 遍历所有Cookie，寻找匹配的Cookie对象
        for (Cookie cookie : cookies) {
            // 检查Cookie名称是否匹配
            if (!cookie.getName().equals(name)) {
                continue;
            }
            // 检查Cookie域名是否与请求服务器名称匹配
//            if (!request.getServerName().equals(cookie.getDomain())){
//                continue;
//            }

            return cookie;
        }

        return null;
    }

    /**
     * 添加一条新的Cookie信息，默认有效时间为一个月
     *
     * @param response HttpServletResponse对象，用于添加Cookie
     * @param name     Cookie的名称
     * @param value    Cookie的值
     */
    public static void setCookie(HttpServletResponse response, String name, String value) {
        setCookie(response, name, value, COOKIE_MAX_AGE);
    }

    /**
     * 添加一条新的Cookie信息，可以设置其最长有效时间(单位：秒)
     *
     * @param response HttpServletResponse对象，用于添加Cookie
     * @param name     Cookie的名称
     * @param value    Cookie的值
     * @param maxAge   Cookie的有效时间，单位为秒，如果设置为0，则使用默认最大有效时间
     */
    public static void setCookie(HttpServletResponse response, String name, String value, int maxAge) {
        // 创建Cookie对象并设置其名称和值
        Cookie cookie = new Cookie(name, value);

        // 根据传入的最大有效时间参数，设置Cookie的最大有效时间
        if (maxAge != 0) {
            cookie.setMaxAge(maxAge);
        } else {
            cookie.setMaxAge(COOKIE_MAX_AGE);
        }

        // 设置Cookie的路径，此处设置为根路径，意味着此Cookie将在整个域名下有效
        cookie.setPath("/");

        // 通过HttpServletResponse对象添加Cookie
        response.addCookie(cookie);
    }

}
